﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

using MyLibrary.ThreeLayer.DataAccess.Enum;
using MyLibrary.ThreeLayer.DataAccess.Attributes;

namespace MyLibrary.ThreeLayer.DataAccess.MetaData
{
    public class MappingCollection : List<MappingField>
    {
        private string _columns = String.Empty;
        private string _columnsWithTables = String.Empty;

        public readonly IList<JoinField> _Joins = new List<JoinField>();
        public readonly IList<Relation> _Relations = new List<Relation>();
        public readonly string _TableName;
        public readonly Type _TargetType;

        public string Columns
        {
            get
            {
                if (String.IsNullOrEmpty(this._columns))
                {
                    this._columns = String.Empty;
                    foreach (MappingField field in this)
                    {
                        if (!String.IsNullOrEmpty(this._columns))
                            this._columns = this._columns + ",";
                        this._columns = this._columns + "[" + field.ColumnName + "]";
                    }
                }
                return this._columns;
            }
        }

        public MappingCollection(Type t)
        {
            this._TargetType = t;
            object[] customAttributes = this._TargetType.GetCustomAttributes(typeof(TableAttribute), true);
            if (customAttributes.Length == 0)
                this._TableName = this._TargetType.Name;
            else
            {
                TableAttribute attribute = (TableAttribute)customAttributes[0];
                this._TableName = attribute._TableName;
                if (String.IsNullOrEmpty(this._TableName))
                    this._TableName = this._TargetType.Name;
            }

            customAttributes = this._TargetType.GetCustomAttributes(typeof(RelationAttribute), true);
            if (customAttributes.Length > 0)
            {
                foreach (RelationAttribute r in customAttributes)
                {
                    Relation item = new Relation(r);
                    this._Relations.Add(item);
                }
            }

            customAttributes = this._TargetType.GetCustomAttributes(typeof(JoinAttribute), true);
            if (customAttributes.Length > 0)
            {
                foreach (JoinAttribute r in customAttributes)
                {
                    Join join = new Join(r);
                    this._Joins.Add(join);
                }
            }
            this.InitPublicProperty();
        }

        public Relation GetChildRelation(Type t)
        {
            foreach (Relation relation in this._Relations)
            {
                if (((relation._RelationType == RelationType.OneToOne) || (relation._RelationType == RelationType.OneToMany)) && (relation._RelationTarget == t))
                    return relation;
            }
            return null;
        }

        public IList<Relation> GetChildRelation()
        {
            IList<Relation> list = new List<Relation>();
            foreach (Relation relation in this._Relations)
            {
                if (relation._RelationType == RelationType.OneToOne || relation._RelationType == RelationType.OneToMany)
                    list.Add(relation);
            }
            return list;
        }

        public string GetColumnName(string propertyName)
        {
            foreach (MappingField field in this)
            {
                if (field.Name == propertyName)
                    return field.ColumnName;
            }
            return null;
        }

        public string GetColumnsWithTables()
        {
            if (String.IsNullOrEmpty(this._columnsWithTables))
            {
                this._columnsWithTables = String.Empty;
                foreach (MappingField field in this)
                {
                    string str;
                    if (!String.IsNullOrEmpty(this._columnsWithTables))
                        this._columnsWithTables = this._columnsWithTables + ",";
                    if (field.Owner == this._TargetType)
                    {
                        str = this._columnsWithTables;
                        this._columnsWithTables = str + "[" + this._TableName + "].[" + field.ColumnName + "]";
                    }
                    else
                    {
                        MappingCollection collection = MetaDataFactory.GetMappingCollection(field.Owner);
                        str = this._columnsWithTables;
                        this._columnsWithTables = str + "[" + collection._TableName + "].[" + field.ColumnName + "]";
                    }
                }
            }
            return this._columnsWithTables;
        }

        public MappingField GetField(string name)
        {
            string str = name.ToLower();
            foreach (MappingField field in this)
            {
                if (field.Name.ToLower() == str)
                    return field;
            }
            return null;
        }

        public MappingField GetFieldByColumnName(string colName)
        {
            string str = colName.ToLower();
            foreach (MappingField field in this)
            {
                if (field.ColumnName.ToLower() == str)
                    return field;
            }
            return null;
        }

        public IList<MappingField> GetIdentityFields()
        {
            IList<MappingField> list = new List<MappingField>();
            foreach (MappingField field in this)
            {
                if (field.IsPrimaryKey && (field.KeyGenerateType == PrimaryKeyType.Identity))
                    list.Add(field);
            }
            return list;
        }

        public IList<MappingField> GetInsertFields()
        {
            IList<MappingField> list = new List<MappingField>();
            if (this._Joins.Count <= 0)
            {
                foreach (MappingField field in this)
                {
                    if (!field.IsPrimaryKey || (field.KeyGenerateType != PrimaryKeyType.Identity))
                        list.Add(field);
                }
            }
            return list;
        }

        public IList<MappingField> GetKeyFields()
        {
            IList<MappingField> list = new List<MappingField>();
            foreach (MappingField field in this)
            {
                if (field.IsPrimaryKey)
                    list.Add(field);
            }
            return list;
        }

        public Relation GetParentRelation(Type t)
        {
            foreach (Relation relation in this._Relations)
            {
                if (((relation._RelationType == RelationType.ManyToOne) || (relation._RelationType == RelationType.ManyToMany)) && (relation._RelationTarget == t))
                    return relation;
            }
            return null;
        }

        public IList<Relation> GetParentRelation()
        {
            IList<Relation> list = new List<Relation>();
            foreach (Relation relation in this._Relations)
            {
                if (relation._RelationType == RelationType.ManyToOne || relation._RelationType == RelationType.ManyToMany)
                    list.Add(relation);
            }
            return list;
        }

        public MappingField GetRefField(Type t)
        {
            foreach (MappingField field in this)
            {
                if (field.RefEntityType == t)
                    return field;
            }
            return null;
        }

        public Relation GetRelation(Type t)
        {
            foreach (Relation relation in this._Relations)
            {
                if (relation._RelationTarget == t)
                    return relation;
            }
            return null;
        }

        public Relation GetRelationByModeAndType(RelationType mode, Type relationType)
        {
            foreach (Relation relation in this)
            {
                if (relation._RelationType == mode && relation._RelationTarget == relationType)
                    return relation;
            }
            return null;
        }

        public Relation GetRelationByMode(RelationType mode, Type relationType)
        {
            IList<Relation> list = new List<Relation>();
            foreach (Relation relation in this)
            {
                if (relation._RelationType == mode)
                    list.Add(relation);
            }
            return list;
        }

        public List<MappingField> GetUpdateFields()
        {
            IList<MappingField> list = new List<MappingField>();
            if (this._Joins.Count <= 0)
            {
                foreach (MappingField field in this)
                {
                    if (!field.IsPrimaryKey)
                        list.Add(field);
                }
            }
            return list;
        }

        private void InitPublicProperty()
        {
            PropertyInfo[] propertyInfos = this._TargetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            this.Parser(propertyInfos);
        }

        private void Parser(PropertyInfo[] properties)
        {
            foreach (PropertyInfo info in properties)
            {
                MappingField mappingField = MetaDataFactory.GetMappingField(info);
                if (mappingField != null)
                    base.Add(mappingField);
            }
        }
    }
}
